/* 
 * File:   Vector3D.h
 * Author: brady
 *
 * Created on June 6, 2011, 11:23 PM
 */

#ifndef VECTOR3D_H
#define	VECTOR3D_H
#include <math.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>

using namespace std;
/**
 * A mutable 3D Vector class
 */
class Vector3D {
public:

    /**
     * Default constructor. Initializes all values to 0
     */
    Vector3D(){
        memset(data, 0, 3*sizeof(double));
    }

    Vector3D(const double xval, const double yval, const double zval){
        data[0] = xval;
        data[1] = yval;
        data[2] = zval;
    }

    const double& operator[](const int i) const
    { 
      return data[i];
    }
    
    double& operator[](const int i)
    { 
      return data[i];
    }

    /**
     * Destructively normalizes the Vector3D
     * @return a reference to the Vector3D after normalization
     */
    Vector3D& normalize();

    /**
     * Calculates the dot product between two vectors
     * @return
     */
    double dot(const Vector3D& v) const;

    /**
     * Calculates the Cross product between two vectors
     * @param v the Vector3D to be crossed with
     * @return the Vector3D
     */
    Vector3D cross(const Vector3D& v) const ;

    /**
     * Computes the magnitude of the Vector3D
     * @return the magnitude of the Vector3D
     */
    double magnitude() const;

    /**
     * Multiplies each channel of this vector
     * with the same channel of the given vector
     * @param v Vector3D to mix with
     * @return the mixed vector
     */
    Vector3D mix(const Vector3D& v) const;

    /**
     * Computes a weighted average Vector3D by mixing this vector
     * scaled by the inverse of weight with the given vector scaled by weight
     * @param v the vector to mix this vector with
     * @param weight the scale factor for the given vector
     * @return a weighted average vector
     */
    Vector3D weightedAverage(const Vector3D& v, double weight) const ;

    /**
     * Destructively caps the data values of this vector to be less than
     * or equal to the given value
     * @param value the maximum value for this vector
     * @return this vector
     */
    Vector3D& cap(double value);

    Vector3D bisect(const Vector3D& v) const;

    const string toString() const;
    
    /**
     * Assignment operator
     * @param rhs Vector3D with the new values
     * @return this
     */
    const Vector3D & operator=(const Vector3D &rhs) {
        data[0] = rhs.data[0];
        data[1] = rhs.data[1];
        data[2] = rhs.data[2];
        return *this;
    }

    /**
     * Addition and assignment operator
     * @param rhs Vector to be summed into this vector
     * @return this
     */
    Vector3D & operator+=(const Vector3D &rhs) {
        data[0] += rhs.data[0];
        data[1] += rhs.data[1];
        data[2] += rhs.data[2];
        return *this;
    }

    /**
     * Subtraction and assignment operator
     * @param rhs Vector3D to be subtracted from this vector
     * @return this
     */
    Vector3D & operator-=(const Vector3D &rhs) {
        data[0] -= rhs.data[0];
        data[1] -= rhs.data[1];
        data[2] -= rhs.data[2];
        return *this;
    }

    /**
     * Constant multiplication operator
     * @param c constant double to multiply this vector by
     * @return this
     */
    Vector3D & operator*=(const double c) {
        data[0] = data[0]*c;
        data[1] = data[1]*c;
        data[2] = data[2]*c;
        return *this;
    }

    /**
     * Constant division operator
     * @param c constant double to divide this vector by
     * @return this
     */
    Vector3D & operator/=(const double c) {
        data[0] = data[0]/c;
	data[1] = data[1]/c;
	data[2] = data[2]/c;
	return *this;
    }

protected:
    double data[3];

private:

};

const Vector3D operator+(const Vector3D& lhs, const Vector3D& rhs);
const Vector3D operator-(const Vector3D& lhs, const Vector3D& rhs);
const Vector3D operator*(const Vector3D& lhs, const double rhs);
const Vector3D operator*(const double rhs, const Vector3D &lhs);
const Vector3D operator/(const Vector3D& lhs, const double rhs);
bool operator<(const Vector3D& lhs, const Vector3D& rhs);
bool operator<=(const Vector3D& lhs, const Vector3D& rhs);
bool operator>(const Vector3D& lhs, const Vector3D& rhs);
bool operator>=(const Vector3D& lhs, const Vector3D& rhs);



#endif	/* VECTOR3D_H */

